﻿using System;
using System.Threading.Tasks;
using Devonline.Core;
using Devonline.Entity;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;

namespace Devonline.MessageClient
{
    public class Client
    {
        private static bool _isRunning = false;
        private HubConnection _connection;
        //private readonly ILogger<Client<T>> _logger;
        private readonly ClientSetting _clientSetting;

        public Client(string server, string clientId)
        {
            //_logger = logger;
            _clientSetting = new ClientSetting { Server = server, ClientId = clientId };
            Init();
        }

        /// <summary>
        /// IM 服务器地址
        /// </summary>
        public string Server => _clientSetting.Server;
        /// <summary>
        /// IM 客户端编号
        /// </summary>
        public string ClientId => _clientSetting.ClientId;
        /// <summary>
        /// IM 本地链接地址
        /// </summary>
        public string Url => Server + "?clientId=" + ClientId;
        /// <summary>
        /// 链接状态
        /// </summary>
        public HubConnectionState ConnectionState => _connection.State;

        /// <summary>
        /// 初始化
        /// </summary>
        private void Init()
        {
            try
            {
                //_logger.LogInformation("IM Client will connect {Server} use {ClientId}", Server, ClientId);

                var builder = new HubConnectionBuilder()
                    .WithUrl(Url, HttpTransportType.WebSockets)
                    .WithAutomaticReconnect();

                builder.AddNewtonsoftJsonProtocol(config => config.PayloadSerializerSettings = AppSettings.JsonSerializerSettings);

                _connection = builder.Build();
            }
            catch (Exception ex)
            {
                //_logger.LogError(ex, "IM Client initial error");
            }
        }

        /// <summary>
        /// 启动连接
        /// </summary>
        /// <returns></returns>
        public async Task StartAsync()
        {
            _isRunning = true;

            try
            {
                //_logger.LogWarning("IM Client now will start to connect the server {Server} use {ClientId}", Server, ClientId);

                //启动监控
                await Task.Factory.StartNew(async () =>
                {
                    await Task.Delay(AppSettings.UNIT_ONE * AppSettings.UNIT_THOUSAND);
                    //_logger.LogWarning("IM Client {State} to the server {Server} use {ClientId}, now will start the monitor", _connection.State, Server, ClientId);
                    await Monitor();
                }, TaskCreationOptions.LongRunning);

                //启动连接
                await _connection.StartAsync();
            }
            catch (Exception ex)
            {
                //_logger.LogError(ex, "IM Client {ClientId} start to connect server {Server} error", ClientId, Server);
            }
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        public async Task StopAsync()
        {
            try
            {
                _isRunning = false;
                await _connection.StopAsync();
                await _connection.DisposeAsync();
                //_logger.LogWarning("IM Client {ClientId} now closed the server {Server}", ClientId, Server);
            }
            catch (Exception ex)
            {
                //_logger.LogError(ex, "IM Client {ClientId} close the server {Server} error", ClientId, Server);
            }
        }

        /// <summary>
        /// 客户端连接监控
        /// </summary>
        /// <returns></returns>
        private async Task Monitor()
        {
            while (_isRunning)
            {
                try
                {
                    if (_connection.State == HubConnectionState.Disconnected)
                    {
                        //_logger.LogWarning("IM Client disconnected to server {Server} use {ClientId}, now will reconnect", Server, ClientId);
                        await _connection.StartAsync();
                    }
                }
                catch (Exception ex)
                {
                    //_logger.LogError(ex, "IM Client connect the server {Server} use {ClientId} error}", Server, ClientId);
                }
                finally
                {
                    await Task.Delay(1000);
                }
            }
        }

        public async Task SendAsync(Message message)
        {
            message.Id ??= KeyGenerator.GetKey<string>();
            message.Sender ??= ClientId;
            message.SendTime ??= DateTime.UtcNow;
            message.Type = MessageType.Text;
            await _connection.InvokeAsync("Send", message);
        }
    }
}
